package com.yds.ydsdatasetbackend.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yds.ydsdatasetbackend.exception.BusinessException;
import com.yds.ydsdatasetbackend.exception.ErrorCode;
import com.yds.ydsdatasetbackend.exception.ThrowUtils;
import com.yds.ydsdatasetbackend.manager.rabbitmq.info.MyFileMessageProducer;
import com.yds.ydsdatasetbackend.manager.upload.FileUpload;
import com.yds.ydsdatasetbackend.model.dto.file.FileEditRequest;
import com.yds.ydsdatasetbackend.model.dto.file.FileQueryRequest;
import com.yds.ydsdatasetbackend.model.dto.file.FileUploadRequest;
import com.yds.ydsdatasetbackend.model.entity.*;
import com.yds.ydsdatasetbackend.model.enums.FileFillEnum;
import com.yds.ydsdatasetbackend.model.enums.FileUploadBizEnum;
import com.yds.ydsdatasetbackend.model.vo.FileVO;
import com.yds.ydsdatasetbackend.model.vo.UserVO;
import com.yds.ydsdatasetbackend.service.*;
import com.yds.ydsdatasetbackend.mapper.FileMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
* @author YDS
* @description 针对表【file(数据文件)】的数据库操作Service实现
* @createDate 2025-01-31 12:05:39
*/
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File>
    implements FileService{

    @Resource
    private UserService userService;

    @Resource
    private FileUpload fileUpload;

    @Resource
    @Lazy
    private FileVariablesService fileVariablesService;

    @Resource
    @Lazy
    private FileTrainService fileTrainService;

    @Resource
    private MyFileMessageProducer myFileMessageProducer;

    @Resource
    private FilePredictService filePredictService;

    @Override
    public void validFile(File file) {
        ThrowUtils.throwIf(file == null, ErrorCode.PARAMS_ERROR);
        // 从对象中取值
        Long id = file.getId();
        String url = file.getUrl();
        String introduction = file.getIntroduction();
        // 修改数据时，id 不能为空，有参数则校验
        ThrowUtils.throwIf(ObjUtil.isNull(id), ErrorCode.PARAMS_ERROR, "id 不能为空");
        if (StrUtil.isNotBlank(url)) {
            ThrowUtils.throwIf(url.length() > 1024, ErrorCode.PARAMS_ERROR, "url 过长");
        }
        if (StrUtil.isNotBlank(introduction)) {
            ThrowUtils.throwIf(introduction.length() > 800, ErrorCode.PARAMS_ERROR, "简介过长");
        }
    }

    @Override
    public FileVO uploadFile(Object inputSource, FileUploadRequest fileUploadRequest, User loginUser) {
        // 校验参数
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);

        // 用于判断是新增还是更新文件
        Long fileId = null;
        if (fileUploadRequest != null) {
            fileId = fileUploadRequest.getId();
        }

        File oldFile = new File();
        // 如果是更新图片，需要校验文件是否存在
        if (fileId != null) {
            oldFile = this.getById(fileId);
            ThrowUtils.throwIf(oldFile == null, ErrorCode.NOT_FOUND_ERROR, "文件不存在");
            // 仅本人或管理员可编辑文件
            if(!oldFile.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }

        // 获取文件大小
        MultipartFile multipartFile = (MultipartFile) inputSource;
        long fileSize = multipartFile.getSize();
        String fileName = multipartFile.getOriginalFilename();
        // 上传图片，得到信息
        String uploadPathPrefix = String.format("/%s/%s", FileUploadBizEnum.PUBLIC_FILE.getValue(), loginUser.getId());
        String uploadFilePath = fileUpload.uploadFile(inputSource, uploadPathPrefix, oldFile.getUrl());
        File file = new File();
        file.setUrl(uploadFilePath);
        file.setName(fileName);
        file.setCategory("默认");
        file.setFileSize(fileSize);
        file.setUserId(loginUser.getId());
        file.setFillStatus(FileFillEnum.WAITING.getValue());
        file.setFileFormat(FileUtil.getSuffix(fileName));

        boolean result = this.saveOrUpdate(file);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败");

        // 发送请求给rabbitmq,让它处理信息
        Long fileId1 = file.getId();
        myFileMessageProducer.sendMessage("file_exchange","my_routingKey", String.valueOf(fileId1));


        return FileVO.objToVo(file);
    }

    @Override
    public FileVO getFileVO(File file, HttpServletRequest request) {
        // 对象转封装类
        FileVO fileVO = FileVO.objToVo(file);
        // 关联查询用户信息
        Long userId = file.getUserId();
        if (userId != null && userId > 0) {
            User user = userService.getById(userId);
            UserVO userVO = userService.getUserVO(user);
            fileVO.setUser(userVO);
        }
        return fileVO;
    }

    /**
     * 分页获取图片封装,将获取到的页进行脱敏
     */
    @Override
    public Page<FileVO> getFileVOPage(Page<File> filePage, HttpServletRequest request) {
        List<File> fileList = filePage.getRecords();
        Page<FileVO> fileVOPage = new Page<>(filePage.getCurrent(), filePage.getSize(), filePage.getTotal());
        if (CollUtil.isEmpty(fileList)) {
            return fileVOPage;
        }
        // 对象列表 => 封装对象列表
        List<FileVO> fileVOList = fileList.stream().map(FileVO::objToVo).collect(Collectors.toList());
        // 1. 关联查询用户信息
        Set<Long> userIdSet = fileList.stream().map(File::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 2. 填充信息
        fileVOList.forEach(fileVO -> {
            Long userId = fileVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            fileVO.setUser(userService.getUserVO(user));
        });
        fileVOPage.setRecords(fileVOList);
        return fileVOPage;
    }

    /**
     * 分页获取图片包装
     *
     * @param fileQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<File> getQueryWrapper(FileQueryRequest fileQueryRequest) {
        QueryWrapper<File> queryWrapper = new QueryWrapper<>();
        if (fileQueryRequest == null) {
            return queryWrapper;
        }
        // 从对象中取值
        Long id = fileQueryRequest.getId();
        String name = fileQueryRequest.getName();
        String introduction = fileQueryRequest.getIntroduction();
        String category = fileQueryRequest.getCategory();
        List<String> tags = fileQueryRequest.getTags();
        Long fileSize = fileQueryRequest.getFileSize();
        Long features = fileQueryRequest.getFeatures();
        Long instances = fileQueryRequest.getInstances();
        String fillStatus = fileQueryRequest.getFillStatus();
        String fileFormat = fileQueryRequest.getFileFormat();
        Long userId = fileQueryRequest.getUserId();
        String searchText = fileQueryRequest.getSearchText();
        Date startEditTime = fileQueryRequest.getStartEditTime();
        Date endEditTime = fileQueryRequest.getEndEditTime();
        String sortField = fileQueryRequest.getSortField();
        String sortOrder = fileQueryRequest.getSortOrder();



        // 从多字段中搜索
        if (StrUtil.isNotBlank(searchText)) {
            // 需要拼接查询条件
            queryWrapper.and(qw -> qw.like("name", searchText)
                    .or()
                    .like("introduction", searchText)
            );
        }
        queryWrapper.eq(ObjUtil.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjUtil.isNotEmpty(userId), "userId", userId);
        queryWrapper.like(StrUtil.isNotBlank(name), "name", name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), "introduction", introduction);
        queryWrapper.like(StrUtil.isNotBlank(fileFormat), "fileFormat", fileFormat);
        queryWrapper.like(StrUtil.isNotBlank(fillStatus), "fillStatus", fillStatus);
        queryWrapper.eq(StrUtil.isNotBlank(category), "category", category);
        queryWrapper.eq(ObjUtil.isNotEmpty(features), "features", features);
        queryWrapper.eq(ObjUtil.isNotEmpty(instances), "instances", instances);
        queryWrapper.eq(ObjUtil.isNotEmpty(fileSize), "fileSize", fileSize);
        queryWrapper.ge(ObjUtil.isNotEmpty(startEditTime), "editTime", startEditTime);
        queryWrapper.lt(ObjUtil.isNotEmpty(endEditTime), "editTime", endEditTime);
        // JSON 数组查询
        if (CollUtil.isNotEmpty(tags)) {
            for (String tag : tags) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        // 排序
        queryWrapper.orderBy(StrUtil.isNotEmpty(sortField), sortOrder.equals("ascend"), sortField);
        return queryWrapper;
    }

    @Override
    public void editFile(FileEditRequest fileEditRequest, User loginUser) {
        // 在此处将实体类和 DTO 进行转换
        File file = new File();
        BeanUtils.copyProperties(fileEditRequest, file);
        // 校验权限
        checkFileAuth(loginUser, this.getById(fileEditRequest.getId()));
        // 注意将 list 转为 string
        file.setTags(JSONUtil.toJsonStr(fileEditRequest.getTags()));
        // 设置编辑时间
        file.setEditTime(new Date());
        // 数据校验
        this.validFile(file);
        // 判断是否存在
        long id = fileEditRequest.getId();
        File oldFile = this.getById(id);
        ThrowUtils.throwIf(oldFile == null, ErrorCode.NOT_FOUND_ERROR);
        // 操作数据库
        boolean result = this.updateById(file);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    }

    @Override
    public void checkFileAuth(User loginUser, File file) {
        Long loginUserId = loginUser.getId();
        if (!loginUserId.equals(file.getUserId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
    }

    @Override
    public void deleteFile(long fileId, User loginUser) {
        ThrowUtils.throwIf(fileId <= 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
        // 判断是否存在
        File oldFile = this.getById(fileId);
        ThrowUtils.throwIf(oldFile == null, ErrorCode.NOT_FOUND_ERROR);
        // 校验权限
        checkFileAuth(loginUser, oldFile);

//        // 操作数据库
        List<Long> fileVariablesList = fileVariablesService.lambdaQuery()
                .eq(FileVariables::getFileId, fileId)
                .list().stream().map(FileVariables::getId).collect(Collectors.toList());


        List<Long> fileTrainList = fileTrainService.lambdaQuery()
                .eq(FileTrain::getFileId, fileId)
                .list().stream().map(FileTrain::getId).collect(Collectors.toList());

        List<Long> filePredictList = filePredictService.lambdaQuery()
                .eq(FilePredict::getFileId, fileId)
                .list().stream().map(FilePredict::getId).collect(Collectors.toList());



        // 删除文件对应的训练信息
        if(!filePredictList.isEmpty()) {
            boolean result = filePredictService.removeBatchByIds(filePredictList);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        }

        if(!fileTrainList.isEmpty()) {
            boolean result = fileTrainService.removeBatchByIds(fileTrainList);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        }

        if(!fileVariablesList.isEmpty()) {
            boolean result1 = fileVariablesService.removeBatchByIds(fileVariablesList);
            ThrowUtils.throwIf(!result1, ErrorCode.OPERATION_ERROR);
        }
        boolean result2 = this.removeById(fileId);
        ThrowUtils.throwIf(!result2, ErrorCode.OPERATION_ERROR);
        // 异步清理文件
//        this.clearPictureFile(oldPicture);
    }

}




